package cfca.seal.web.service;

import cfca.sadk.algorithm.common.GenKeyAttribute;
import cfca.sadk.algorithm.common.Mechanism;
import cfca.sadk.algorithm.common.PKIException;
import cfca.sadk.algorithm.sm2.SM2PrivateKey;
import cfca.sadk.lib.crypto.JCrypto;
import cfca.sadk.lib.crypto.Session;
import cfca.sadk.util.Base64;
import cfca.sadk.util.KeyUtil;
import cfca.sadk.x509.certificate.X509Cert;
import cfca.seal.bean.AdminUser;
import cfca.seal.bean.SealCert;
import cfca.seal.dao.main.ISealCertDao;
import cfca.seal.dao.main.ISealDao;
import cfca.seal.exception.CodeException;
import cfca.seal.mananger.SealCertManager;
import cfca.seal.system.Env;
import cfca.seal.util.P10Helper;
import cfca.seal.util.SealCertUtil;
import cfca.seal.util.StringUtil;
import java.security.Key;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;

@Service
public class SealCertService
{

  @Resource
  private ISealCertDao sealCertDao;

  @Resource
  private ISealDao sealDao;

  public List<SealCert> getSealCerts(AdminUser manager)
    throws Exception
  {
    List<SealCert> list = this.sealCertDao.getAll(manager.getUserType() > 0 ? manager.getOrganizationId() : "");
    for (SealCert sealCert : list) {
      addAdditionInfo(sealCert);
    }
    return list;
  }

  public SealCert getSealCertById(String id) throws Exception {
    return this.sealCertDao.getSealCertById(id);
  }

  public SealCert getSealCertByKeyID(String id) throws Exception {
    return this.sealCertDao.getSealCertByKeyID(id);
  }

  public boolean deleteById(String id) throws Exception {
    List list = this.sealDao.getSealBySealCertId(id);
    if ((null != list) && (list.size() > 0)) {
      throw new CodeException("C7009", "印章证书已被制章,无法删除");
    }

    if ("sqlite".equals(Env.databaseType)) {
      SealCertManager.getInstance().removeSealCert(id);
    }
    return this.sealCertDao.deleteById(id);
  }

  public void update(SealCert sealCert) throws Exception {
    this.sealCertDao.update(sealCert);
  }

  public boolean addSealCert(SealCert sealCert) throws Exception {
    return this.sealCertDao.insert(sealCert);
  }

  public void generalReq(SealCert sealCert, String password) throws Exception {
    String keyALg = "RSA";
    String signAlg = "sha256WithRSAEncryption";
    if ("SM3SM2".equals(sealCert.getCertAlg())) {
      keyALg = "SM2";
      signAlg = "sm3WithSM2Encryption";
    }

    int keyLength = 0;
    if ("JSOFT_LIB".equals(sealCert.getDevice())) {
      keyLength = Integer.parseInt(sealCert.getKeyLength());
    }

    Mechanism mechanism = new Mechanism(keyALg);
    if ("JHARD_LIB".equalsIgnoreCase(sealCert.getDevice())) {
      GenKeyAttribute attr = new GenKeyAttribute();
      attr.isExport = false;
      attr.keyNum = Integer.parseInt(sealCert.getKeyId());
      mechanism.setParam(attr);
    }

    byte[] p10 = null;
    KeyPair keyPair = null;
    try {
      P10Helper helper = new P10Helper();
      Session session = helper.openSession(sealCert.getDevice());
      keyPair = helper.buildKeyPair(session, mechanism, keyLength);
      p10 = helper.buildP10(session, keyPair, signAlg);
    } catch (Exception e) {
      throw new CodeException("C6001", "产生证书申请书错误", e);
    }

    if (StringUtil.isNotEmpty(password)) {
      sealCert.setPassword(cfca.seal.util.EncryptUtil.encrypto(password));
    }
    if ("JSOFT_LIB".equalsIgnoreCase(sealCert.getDevice())) {
      PrivateKey priKey = keyPair.getPrivate();
      byte[] data = priKey.getEncoded();
      if ("SM3SM2".equals(sealCert.getCertAlg())) {
        SM2PrivateKey sm2PrivateKey = (SM2PrivateKey)priKey;
        data = sm2PrivateKey.getDByBytesWithPublicKey();
      }
      byte[] privateKeyEncryptedData;
      try {
        privateKeyEncryptedData = cfca.sadk.util.EncryptUtil.encryptMessageBySM4(Base64.encode(data), password);
      } catch (PKIException e) {
        throw new CodeException("C6002", "加密数据失败", e);
      }
      sealCert.setPrivateKeyEncryptedData(privateKeyEncryptedData);
    }
    sealCert.setP10(p10);
    sealCert.setStatus("1");
    update(sealCert);
  }

  public void importCert(byte[] certData, SealCert sealCert, String confirmPassword) throws Exception
  {
    decryptCiphertext(sealCert);

    if (("JSOFT_LIB".equalsIgnoreCase(sealCert.getDevice())) && (!sealCert.getPasswordPlain().equals(confirmPassword))) {
      throw new CodeException("C6003", "私钥保护密码不正确");
    }

    String signALg = "";
    String keyAlg = "";

    if ("SHA1RSA".equals(sealCert.getCertAlg())) {
      signALg = "sha1WithRSAEncryption";
      keyAlg = "RSA";
    } else {
      signALg = "sm3WithSM2Encryption";
      keyAlg = "SM2";
    }

    Key priKey = null;
    Key pubKey = null;
    if ("JSOFT_LIB".equalsIgnoreCase(sealCert.getDevice())) {
      if ("SHA1RSA".equals(sealCert.getCertAlg())) {
        priKey = SealCertUtil.getRSAPrivateKey(Base64.decode(sealCert.getPrivateKeyData()));
      } else {
        priKey = SM2PrivateKey.getInstance(Base64.decode(sealCert.getPrivateKeyData()));
      }
    }
    else {
      Mechanism mechanism = new Mechanism(keyAlg);
      GenKeyAttribute attr = new GenKeyAttribute();
      attr.isExport = false;
      attr.keyNum = Integer.parseInt(sealCert.getKeyId());
      mechanism.setParam(attr);
      try {
        JCrypto.getInstance().initialize(sealCert.getDevice(), null);
        Session session = JCrypto.getInstance().openSession(sealCert.getDevice());
        KeyPair keyPair = KeyUtil.generateKeyPair(mechanism, 2048, session);
        priKey = keyPair.getPrivate();
        pubKey = keyPair.getPublic();
      } catch (Exception e) {
        throw new CodeException("C6003", "私钥保护文件的密码不正确", e);
      }

    }

    X509Cert x509Cert = null;
    try {
      x509Cert = new X509Cert(certData);
    } catch (PKIException ex1) {
      throw new CodeException("C6004", "X509Cert证书初始化数据失败", ex1);
    }

    SealCertUtil.checkCertMatchKey(signALg, sealCert.getDevice(), x509Cert, (PrivateKey)priKey, (PublicKey)pubKey);
    sealCert.setPublickCertData(certData);

    sealCert.setStatus("2");
    update(sealCert);
    if ("sqlite".equals(Env.databaseType)) {
      SealCertManager.getInstance().addSealCert(sealCert);
    }
  }

  public void addAdditionInfo(SealCert sealCert) throws CodeException
  {
    if ("2".equals(sealCert.getStatus())) {
      try {
        X509Cert x509Cert = new X509Cert(sealCert.getPublickCertData());
        sealCert.setNotBefore(x509Cert.getNotBefore());
        sealCert.setNotAfter(x509Cert.getNotAfter());
        sealCert.setDN(x509Cert.getSubject());
        sealCert.setIssuer(x509Cert.getIssuer());
      } catch (Exception e) {
        e.printStackTrace();
        throw new CodeException("C6004", "X509Cert证书初始化数据失败", e);
      }
    }
  }

  public void decryptCiphertext(SealCert sealCert) throws CodeException
  {
    try {
      if (StringUtil.isNotEmpty(sealCert.getPassword())) {
        sealCert.setPasswordPlain(cfca.seal.util.EncryptUtil.decrypto(sealCert.getPassword()));
      }

      if (null != sealCert.getPrivateKeyEncryptedData()) {
        String privateKeyData = new String(cfca.sadk.util.EncryptUtil.decryptMessageBySM4(sealCert.getPrivateKeyEncryptedData(), sealCert.getPasswordPlain()));

        sealCert.setPrivateKeyData(privateKeyData);
      }
    } catch (Exception e) {
      throw new CodeException("C6005", "解密加密数据失败", e);
    }
  }
}